home *** CD-ROM | disk | FTP | other *** search
Text File | 2009-11-03 | 36.2 KB | 1,046 lines |
- #!/usr/bin/python
- # coding=utf-8
-
- '''
- gnome-about
-
- # Pretty About Dialog for the GNOME Desktop #
-
- Copyright (C) 2007 Guillaume Seguin <guillaume@segu.in>
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
-
- Authors:
- Guillaume Seguin <guillaume@segu.in>
- Vincent Untz <vuntz@gnome.org> (get_language_names () helper function)
- '''
-
- import pygtk
- pygtk.require ('2.0')
-
- import gobject
- from gobject.option import OptionParser, make_option
- import gtk
-
- import cairo
- from math import pi
-
- import os, sys, random, time, gettext, locale
-
- import xml.dom.minidom
-
- DESCRIPTION_DELAY = 10000
- CONTRIBUTOR_DELAY = 2500
-
- PACKAGE = "gnome-desktop"
- VERSION = "2.28.1"
- GETTEXT_PACKAGE = "gnome-desktop-2.0"
-
- LOCALEDIR = "/usr/share/locale"
- DATADIR = "/usr/share/gnome-about"
- ICONDIR = "/usr/share/pixmaps"
-
- LOGO_FILE = "gnome-64.png"
-
- gettext.install (GETTEXT_PACKAGE, LOCALEDIR, unicode = True)
-
- header_links = [
- (_("About GNOME"), "http://www.gnome.org/about/"),
- (_("News"), "http://news.gnome.org/"),
- (_("GNOME Library"), "http://library.gnome.org/"),
- (_("Friends of GNOME"), "http://www.gnome.org/friends/"),
- (_("Contact"), "http://www.gnome.org/contact/"),
- ]
-
- translated_contributors = [
- _("The Mysterious GEGL"),
- _("The Squeaky Rubber GNOME"),
- _("Wanda The GNOME Fish")
- ]
-
- default_link_color = gtk.gdk.Color (0, 0, 65535)
-
- def locate_file (file):
- '''Wrap gnome_program_locate_file to avoid ugly duplication'''
- dirnames = [DATADIR, '.']
- for dirname in dirnames:
- filename = os.path.join(dirname, file)
- if os.path.exists(filename):
- return filename
- return False
-
- def cleanup_date (date):
- '''Parse a date as found in gnome-version.xml and nicely format it'''
- try:
- # FIXME: we don't have g_locale_to_utf8 in python. See bug #530382
- return unicode (time.strftime ("%x", time.strptime (date, "%Y-%m-%d")), locale.getpreferredencoding ()).encode ("utf-8")
- except:
- return ""
-
- # Imported from GNOME's Sabayon
- # (sabayon/admin-tool/lockdown/disabledapplets.py)
- # There's no wrapper for g_get_language_names (). Ugly workaround:
- # Note that we don't handle locale alias...
- def get_language_names ():
- if "LANGUAGE" in os.environ.keys () and os.environ["LANGUAGE"] != "":
- env_lang = os.environ["LANGUAGE"].split ()
- elif "LC_ALL" in os.environ.keys () and os.environ["LC_ALL"] != "":
- env_lang = os.environ["LC_ALL"].split ()
- elif "LC_MESSAGES" in os.environ.keys () and os.environ["LC_MESSAGES"] != "":
- env_lang = os.environ["LC_MESSAGES"].split ()
- elif "LANG" in os.environ.keys () and os.environ["LANG"] != "":
- env_lang = os.environ["LANG"].split ()
- else:
- env_lang = []
-
- env_lang.reverse ()
- languages = []
-
- for language in env_lang:
- start_pos = 0
- mask = 0
- uscore_pos = language.find ("_")
- if uscore_pos != -1:
- start_pos = uscore_pos
- mask += 1 << 2
- dot_pos = language.find (".", start_pos)
- if dot_pos != -1:
- start_pos = dot_pos
- mask += 1 << 0
- at_pos = language.find ("@", start_pos)
- if at_pos != -1:
- start_pos = at_pos
- mask += 1 << 1
-
- if uscore_pos != -1:
- lang = language[:uscore_pos]
- elif dot_pos != -1:
- lang = language[:dot_pos]
- elif at_pos != -1:
- lang = language[:at_pos]
- else:
- lang = language
-
- if uscore_pos != -1:
- if dot_pos != -1:
- territory = language[uscore_pos:dot_pos]
- elif at_pos != -1:
- territory = language[uscore_pos:at_pos]
- else:
- territory = language[uscore_pos:]
- else:
- territory = ""
-
- if dot_pos != -1:
- if at_pos != -1:
- codeset = language[dot_pos:at_pos]
- else:
- codeset = language[dot_pos:]
- else:
- codeset = ""
-
- if at_pos != -1:
- modifier = language[at_pos:]
- else:
- modifier = ""
-
- for i in range (mask + 1):
- if i & ~mask == 0:
- newlang = lang
- if (i & 1 << 2):
- newlang += territory
- if (i & 1 << 0):
- newlang += codeset
- if (i & 1 << 1):
- newlang += modifier
- languages.insert (0, newlang)
-
- return languages
-
- class GettableList (list):
- '''Dumb wrapper around Python list with a get () method to iterate \
- the list'''
-
- current = 0
-
- def get (self):
- if not len (self):
- return None
- if self.current == -1:
- item = None
- else:
- item = self[self.current]
- self.current += 1
- if self.current == len (self):
- self.current = -1
- else:
- self.current = self.current % len (self)
- return item
-
- class GnomeContributors (GettableList):
- '''Randomized contributors list'''
-
- current = 0
-
- def __init__ (self):
- '''Initiate object and load contributors lists'''
- super (GnomeContributors, self).__init__ ()
- map (self.append, translated_contributors)
- self.load_from_file ("contributors.list")
- self.load_from_file ("foundation-members.list")
- random.shuffle (self) # Randomize list...
-
- def load_from_file (self, file):
- '''Load a list of contributors and validate it'''
- def validate_contributor (contrib):
- try:
- contrib.encode ("utf8")
- return len (contrib) > 0 and contrib[0] != "#"
- except Exception, e:
- print e
- return False
-
- path = locate_file (file)
-
- if not path:
- print '''Warning: "%s" file not found.''' % file
- return
-
- f = open (path, "r")
- try:
- data = f.readlines ()
- finally:
- f.close ()
-
- '''Cleanup list'''
- data = map (lambda s: s.rstrip (), data)
-
- '''Check that the file begins with the correct header'''
- if not data or data[0] != "# gnome-about contributors - format 1":
- return
-
- '''Filter the contributors list and append it'''
- contributors = filter (validate_contributor, data)
- map (self.append, contributors)
-
- def get (self):
- '''Return a contributor from the currently randomized list. \
- If we hit the end of the list, randomize it again'''
- contributors_count = len (self)
- if not contributors_count:
- print "Warning: empty contributors list."
- return
- contributor = self[self.current]
- self.current += 1
- if self.current >= contributors_count:
- '''Remove the last item of the list and reinsert it after \
- shuffling to make sure it won't get displayed twice in a row'''
- self.pop ()
- random.shuffle (self)
- index = random.randint (contributors_count / 2,
- contributors_count)
- self.insert (index, contributor)
- self.current = 0
- return contributor
-
- # Animation is done using:
- # * A custom gtk.Label, for catching mouse press events
- # * A gtk.Alignment, for positionning and scrolling (the actual animation)
- # * A gtk.Layout, so that the Alignment can be wider than what's displayed
- # and thus let the Label appear and disappear smoothly
-
- class AnimatedLabel (gtk.Layout):
- '''Pretty animated label'''
-
- items = None # items must either be a GettableList object
- # or expose a get () method'''
- timeout = 0
- format = "" # format must be a valid formatting string for a single %s
-
- item = None
- next = None
-
- current = None
- label = None
- source = None
- state = 0 # 0 = appearing ; 1 = landed ; 2 = vanishing
- pos = 0.0
-
- width = 0
- height = 0
-
- def __init__ (self, items, width, height, timeout, format = "%s"):
- '''Initiate object'''
- super (AnimatedLabel, self).__init__ ()
- self.items = items
- self.next = self.items.get () # Pop the first item
- self.width = width
- self.height = height
- self.timeout = timeout
- self.format = format
- self.set_size_request (width, height)
- self.connect ("button-press-event", self.on_button_press)
- self.connect ("map", self.reset_animation)
-
- def reset_animation (self, *args):
- '''Reset label and fire animation timer'''
- self.reset_label ()
- if not self.label:
- return
- self.source = gobject.timeout_add (5, self.animate)
-
- def make_label (self):
- '''Build the label widgets'''
- self.label = WindowedLabel ()
- self.label.connect ("button-press-event", self.on_button_press)
- self.label.set_justify (gtk.JUSTIFY_FILL)
- self.label.set_line_wrap (True)
- self.label.set_markup (self.format % self.item)
- self.label.set_selectable (True)
-
- def reset_label (self):
- '''Drop current label if any and create the new one'''
- if self.current:
- self.remove (self.current)
- self.current = None
- self.label = None
- self.item = self.next
- self.next = self.items.get ()
- if not self.item:
- return False
- self.make_label ()
- self.state = -1
-
- def on_button_press (self, widget, event):
- '''Switch to next item upon left click'''
- if event.button != 1 or not self.current:
- return
- # Remove the current timeout if any to avoid bad side effects
- if self.source:
- gobject.source_remove (self.source)
- self.animate ()
- return True
-
- class VertAnimatedLabel (AnimatedLabel):
- '''Vertically animated label'''
-
- rewind_text = ""
- last_label_height = 0
-
- def rewind_animate (self):
- '''Animation function for the rewind step'''
- self.source = None
- if self.state == -2:
- self.item = self.rewind_text
- self.make_label ()
- label_height = self.label.size_request ()[1]
- total_height = self.height + label_height
- self.pos = float (self.last_label_height) / total_height
- self.current.set (0.5, self.pos, 0, 0)
- self.state = 0
- self.source = gobject.timeout_add (10, self.rewind_animate)
- elif self.state == 0:
- if self.pos < 1.0:
- '''Move towards the bottom position'''
- self.pos = min (1.0, self.pos + 0.01)
- self.current.set (0.5, self.pos, 0, 0)
- self.source = gobject.timeout_add (10, self.rewind_animate)
- else:
- '''Bottommost position reached'''
- self.rewind_text = ""
- self.reset_animation ()
- return False
-
- def animate (self):
- '''The actual animation function'''
- self.source = None
- if self.state == -2:
- self.rewind_animate ()
- elif self.state == -1:
- self.rewind_text += "\n\n%s" % self.item
- self.state = 0
- self.animate ()
- elif self.state == 0:
- if self.pos:
- '''Move towards the top position'''
- self.pos = max (0, self.pos - 0.02)
- label_height = self.label.size_request ()[1]
- total_height = self.height + label_height
- real_pos = float (self.pos * self.height + label_height) \
- / total_height
- self.current.set (0.5, real_pos, 0, 0)
- self.source = gobject.timeout_add (5, self.animate)
- else:
- '''Topmost position reached'''
- self.state = 1
- self.pos = 1.0
- self.source = gobject.timeout_add (self.timeout, self.animate)
- elif self.state == 1:
- '''Dont let selected labels vanish until they are unselected'''
- if self.label.get_selection_bounds () == ():
- self.state = 2
- self.source = gobject.timeout_add (5, self.animate)
- elif self.state == 2:
- if not self.next:
- self.state = -2
- self.last_label_height = self.label.size_request ()[1]
- self.reset_animation ()
- self.source = gobject.timeout_add (1, self.animate)
- elif self.pos:
- '''Move out of the visible region of the Layout'''
- self.pos = max (0, self.pos - 0.02)
- label_height = self.label.size_request ()[1]
- total_height = self.height + label_height
- real_pos = float (self.pos * label_height) \
- / total_height
- self.current.set (0.5, real_pos, 0, 0)
- self.source = gobject.timeout_add (5, self.animate)
- else:
- '''Label has disappeared, bye bye'''
- self.reset_animation ()
- return False
-
- def make_label (self):
- '''Build a new label widget'''
- super (VertAnimatedLabel, self).make_label ()
- if not self.label:
- return
- self.label.set_size_request (self.width, -1)
- self.current = gtk.Alignment (0.0, 1.0)
- label_height = self.label.size_request ()[1]
- height = self.size_request ()[1]
- self.current.set_size_request (-1, 2 * label_height + height)
- self.current.add (self.label)
- self.put (self.current, 0, - label_height)
- self.pos = 1.0
- self.show_all ()
-
- class HorzAnimatedLabel (AnimatedLabel):
- '''Horizontally animated label'''
-
- def animate (self):
- '''The actual animation function'''
- self.source = None
- if self.state == -2:
- self.reset_animation ()
- elif self.state <= 0:
- if self.pos != 0.5:
- '''Move towards the center position'''
- self.pos = max (0.5, self.pos - 0.02)
- self.current.set (self.pos, 0.5, 0, 0)
- self.source = gobject.timeout_add (5, self.animate)
- else:
- '''Center position reached, switch to return mode'''
- self.state = 1
- self.source = gobject.timeout_add (self.timeout, self.animate)
- elif self.state == 1:
- '''Dont let selected labels vanish until they are unselected'''
- if self.label.get_selection_bounds () == ():
- self.state = 2
- self.source = gobject.timeout_add (5, self.animate)
- elif self.state == 2:
- if self.pos:
- '''Disappear by moving left'''
- self.pos = max (0, self.pos - 0.02)
- self.current.set (self.pos, 0.5, 0, 0)
- self.source = gobject.timeout_add (5, self.animate)
- else:
- '''Left position reached, let's move on'''
- self.reset_animation ()
- return False
-
- def make_label (self):
- '''Build a new label widget'''
- super (HorzAnimatedLabel, self).make_label ()
- if not self.label:
- return
- self.label.set_size_request (-1, self.height)
- self.current = gtk.Alignment (1.0, 0.0)
- label_width = self.label.size_request ()[0]
- width = self.size_request ()[0]
- self.current.set_size_request (2 * label_width + width, -1)
- self.current.add (self.label)
- self.put (self.current, - label_width, 0)
- self.pos = 1.0
- self.show_all ()
-
- class WindowedLabel (gtk.Label):
- '''Custom gtk.Label with an overlapping input-only gtk.gdk.Window'''
-
- event_window = None
-
- def __init__ (self, debug = False):
- '''Initialize object and plug all signals'''
- self.debug = debug
- super (WindowedLabel, self).__init__ ()
-
- def do_realize (self):
- '''Create a custom GDK window with which we will be able to play'''
- gtk.Label.do_realize (self)
- event_mask = self.get_events () | gtk.gdk.BUTTON_PRESS_MASK \
- | gtk.gdk.BUTTON_RELEASE_MASK \
- | gtk.gdk.KEY_PRESS_MASK
- self.event_window = gtk.gdk.Window (parent = self.get_parent_window (),
- window_type = gtk.gdk.WINDOW_CHILD,
- wclass = gtk.gdk.INPUT_ONLY,
- event_mask = event_mask,
- x = self.allocation.x,
- y = self.allocation.y,
- width = self.allocation.width,
- height = self.allocation.height)
- self.event_window.set_user_data (self)
-
- def do_unrealize (self):
- '''Destroy event window on unrealize'''
- self.event_window.set_user_data (None)
- self.event_window.destroy ()
- gtk.Label.do_unrealize (self)
-
- def do_size_allocate (self, allocation):
- '''Move & resize the event window to fit the Label's one'''
- gtk.Label.do_size_allocate (self, allocation)
- if self.flags () & gtk.REALIZED:
- self.event_window.move_resize (allocation.x, allocation.y,
- allocation.width, allocation.height)
-
- def do_map (self):
- '''Show event window'''
- gtk.Label.do_map (self)
- self.event_window.show ()
- '''Raise the event window to make sure it is over the Label's one'''
- self.event_window.raise_ ()
-
- def do_unmap (self):
- '''Hide event window on unmap'''
- self.event_window.hide ()
- gtk.Label.do_unmap (self)
-
- gobject.type_register (WindowedLabel)
-
- class HyperLink (WindowedLabel):
- '''Clickable www link label'''
-
- url = ""
- menu = None
- selection = None
-
- def __init__ (self, label, url):
- '''Initialize object'''
- super (HyperLink, self).__init__ ()
- markup = "<b><u>%s</u></b>" % label
- self.set_markup (markup)
- self.set_selectable (True)
- self.url = url
- self.create_menu ()
- link_color = self.style_get_property ("link-color")
- if not link_color:
- link_color = default_link_color
- self.modify_fg (gtk.STATE_NORMAL, link_color)
-
- def open_url (self, *args):
- '''Use GNOME API to open the url'''
- try:
- gtk.show_uri (self.get_screen(), self.url, 0)
- except Exception, e:
- print '''Warning: could not open "%s": %s''' % (self.url, e)
-
- def copy_url (self, *args):
- '''Copy URL to Clipboard'''
- clipboard = gtk.clipboard_get ("CLIPBOARD")
- clipboard.set_text (self.url)
-
- def create_menu (self):
- '''Create the popup menu that will be displayed upon right click'''
- self.menu = gtk.Menu ()
- open_item = gtk.ImageMenuItem (_("_Open URL"))
- open_image = gtk.image_new_from_stock (gtk.STOCK_OPEN,
- gtk.ICON_SIZE_MENU)
- open_item.set_image (open_image)
- open_item.connect ("activate", self.open_url)
- open_item.show ()
- self.menu.append (open_item)
- copy_item = gtk.ImageMenuItem (_("_Copy URL"))
- copy_image = gtk.image_new_from_stock (gtk.STOCK_COPY,
- gtk.ICON_SIZE_MENU)
- copy_item.set_image (copy_image)
- copy_item.connect ("activate", self.copy_url)
- copy_item.show ()
- self.menu.append (copy_item)
-
- def display_menu (self, button, time, place = False):
- '''Display utility popup menu'''
- if place:
- alloc = self.get_allocation ()
- pos = self.event_window.get_origin ()
- x = pos[0]
- y = pos[1] + alloc.height
- func = lambda *a: (x, y, True)
- else:
- func = None
- self.menu.popup (None, None, func, button, time)
-
- def do_map (self):
- '''Select the HAND2 cursor on map'''
- WindowedLabel.do_map (self)
- cursor = gtk.gdk.Cursor (gtk.gdk.HAND2)
- self.event_window.set_cursor (cursor)
-
- def do_button_press_event (self, event):
- '''Update selection bounds infos or display popup menu'''
- if event.button == 1:
- self.selection = self.get_selection_bounds ()
- elif event.button == 3:
- self.display_menu (event.button, event.time)
- return True
- WindowedLabel.do_button_press_event (self, event)
-
- def do_button_release_event (self, event):
- '''Open url if selection hasn't changed since initial press'''
- if event.button == 1:
- selection = self.get_selection_bounds ()
- if selection == self.selection:
- self.open_url ()
- return True
- WindowedLabel.do_button_release_event (self, event)
-
- def do_key_press_event (self, event):
- '''Open url when Return key is pressed'''
- if event.keyval == gtk.keysyms.Return:
- self.open_url ()
- return True
- elif event.keyval == gtk.keysyms.Menu \
- or (event.keyval == gtk.keysyms.F10 \
- and event.state & gtk.accelerator_get_default_mod_mask() == \
- gtk.gdk.SHIFT_MASK):
- self.display_menu (event.keyval, event.time, place = True)
- return True
- WindowedLabel.do_key_press_event (self, event)
-
- gobject.type_register (HyperLink)
-
- class GnomeLogo (gtk.Widget):
- '''Simple widget displaying a colored GNOME logo'''
-
- _surface = None
- _parent = None
- _file = None
-
- def __init__ (self, parent, file):
- '''Initialize object and do the initial painting'''
- gtk.Widget.__init__ (self)
-
- self._parent = parent
- self._file = file
-
- self.paint_surface ()
-
- width = self._surface.get_width ()
- height = self._surface.get_height ()
- self.set_size_request (width, height)
-
- self.set_flags(self.flags() | gtk.NO_WINDOW)
-
- def paint_surface (self):
- '''Paint image and color overlay'''
- self._surface = cairo.ImageSurface.create_from_png (self._file)
-
- text_color = self._parent.get_style ().fg[gtk.STATE_NORMAL]
-
- cr = cairo.Context (self._surface)
- cr.set_source_rgb (float (text_color.red) / 65535,
- float (text_color.green) / 65535,
- float (text_color.blue) / 65535)
- cr.set_operator (cairo.OPERATOR_ATOP)
- cr.paint ()
-
- def do_expose_event (self, event):
- '''Paint the saved surface to the widget context'''
- cr = self.window.cairo_create ()
-
- cr.set_operator (cairo.OPERATOR_OVER)
- cr.set_source_surface (self._surface, 10, 10)
- cr.paint ()
-
- def do_style_set (self, *args):
- '''Style changed, let's repaint image'''
- self.paint_surface ()
- self.queue_draw ()
-
- gobject.type_register (GnomeLogo)
-
- class GnomeAboutHeader (gtk.Layout):
- '''Pretty header for gnome-about'''
-
- links = []
-
- width = 0
- height = 0
-
- def __init__ (self, links):
- '''Initialize object, plug map signal'''
- super (GnomeAboutHeader, self).__init__ ()
- self.links = links
-
- def do_realize (self):
- '''Load header and build links'''
- gtk.Layout.do_realize (self)
-
- current_x = 0
- current_y = 0
- base_y = 0
-
- header = self.load_header ()
- if header:
- self.put (header, 0, 0)
- current_y = header.get_pixbuf ().get_height ()
- base_y = current_y + 4
- line = self.create_line ()
- image = gtk.Image ()
- image.set_from_pixmap (line, None)
- self.put (image, 0, current_y)
-
- logo = self.load_logo ()
- if logo:
- self.put (logo, 0, 0)
- logo_size = logo.get_size_request ()
- current_x += logo_size[0] + 25
- current_y = logo_size[1] + 20
-
- dot = self.create_dot ()
-
- def make_link_widget (link):
- '''Helper function which makes an HyperLink and shows it'''
- label = HyperLink (link[0], link[1])
- label.show_all ()
- return label
-
- widgets = map (make_link_widget, self.links)
- put_widgets = 0
- for widget in widgets:
- if put_widgets > 0:
- if dot:
- image = gtk.Image ()
- image.set_from_pixmap (dot, None)
- self.put (image, current_x + 5, base_y + 6)
- current_x += 16
- self.put (widget, current_x, base_y)
- current_x += widget.size_request ()[0]
- put_widgets += 1
-
- self.width = current_x + 10
- self.height = current_y
- self.set_size_request (self.width, self.height)
- self.show_all ()
-
- def load_header (self):
- '''Load a random header image as a gtk.Image'''
-
- directory = locate_file ("headers")
- if not directory:
- print "Warning: header images directory not found."
- return None
-
- try:
- headers = os.listdir (directory)
- except:
- print "Warning: failed to read header images directory."
- return None
-
- headers = filter (lambda s: s[-4:] in (".png", ".gif"), headers)
- header = random.choice (headers)
-
- file = os.path.join (directory, header)
- try:
- pixbuf = gtk.gdk.pixbuf_new_from_file (file)
- except:
- print '''Warning: failed to load header image "%s".''' % file
- return None
-
- image = gtk.Image ()
- image.set_from_pixbuf (pixbuf)
-
- return image
-
- def load_logo (self):
- '''Load a GNOME foot logo as a gtk.Image'''
-
- file = locate_file (LOGO_FILE)
- if not file:
- print '''Warning: GNOME logo file "%s" not found.''' % LOGO_FILE
- return None
-
- try:
- logo = GnomeLogo (self, file)
- except Exception:
- print '''Warning: failed to load GNOME logo image "%s".''' % file
- return None
-
- return logo
-
- def create_dot (self):
- '''Create a pixmap containing a simple dot'''
- pixmap = gtk.gdk.Pixmap (self.window, 6, 6)
- context = pixmap.cairo_create ()
- context.set_operator (cairo.OPERATOR_SOURCE)
- context.set_source_color (self.style.bg[self.state])
- context.paint ()
- context.set_operator (cairo.OPERATOR_OVER)
- context.set_source_color (self.style.fg[self.state])
- context.arc (3, 3, 2.3, 0, 2 * pi)
- context.fill ()
- return pixmap
-
- def create_line (self, width = 2000, height = 1):
- '''Create a pixmap containing a simple line'''
- pixmap = gtk.gdk.Pixmap (self.window, width, height)
- context = pixmap.cairo_create ()
- context.set_operator (cairo.OPERATOR_SOURCE)
- context.set_source_rgb (0, 0, 0)
- context.paint ()
- return pixmap
-
- gobject.type_register (GnomeAboutHeader)
-
- class GnomeAbout (gtk.Dialog):
- '''Super pretty About Dialog for the GNOME Desktop'''
-
- header = None
- contributors = None
- description_messages = GettableList ()
- system_infos = []
-
- def __init__ (self, ui = True):
- '''Initialize underlying gnome.Program, Contributors list, UI...'''
- super (GnomeAbout, self).__init__ (_("About the GNOME Desktop"),
- buttons = (gtk.STOCK_CLOSE,
- gtk.RESPONSE_CLOSE))
-
- # Immediately fetch system infos to load description messages
- self.system_infos = self.get_system_infos ()
-
- if not ui:
- return
-
- self.contributors = GnomeContributors ()
-
- icon_file = ICONDIR + "/gnome-logo-icon-transparent.png"
- try:
- self.set_icon_from_file (icon_file)
- except gobject.GError:
- pass
-
- self.create_ui ()
-
- self.set_default_response (gtk.RESPONSE_CLOSE)
- self.set_position (gtk.WIN_POS_CENTER)
- map (lambda prop: self.set_property (prop[0], prop[1]),
- [("allow-grow", False), ("allow-shrink", False)])
- self.connect ("delete-event", gtk.main_quit)
- self.connect ("response", self.response_callback)
-
- def gnome_version (self):
- '''Output basic GNOME version information to console'''
- def print_info (info):
- infos_dict = {"name": info[0], "value": info[1]}
- # Translators: %(name)s and %(value)s should not be translated:
- # it's a way to identify a string, so just handle them like %s
- print _("%(name)s: %(value)s") % infos_dict
- map (print_info, self.system_infos)
-
- def create_ui (self):
- '''Fill our Dialog with some lovely widgets'''
- self.set_has_separator (False)
- main_box = self.get_children ()[0] # Get the internal Dialog VBox
-
- '''Pretty header'''
- self.header = GnomeAboutHeader (header_links)
- main_box.pack_start (self.header)
-
- welcome_label = WindowedLabel ()
- welcome_label.set_markup ("<big><big><b>%s</b></big></big>" % \
- _("Welcome to the GNOME Desktop"))
- main_box.pack_start (welcome_label)
-
- descriptions_label = VertAnimatedLabel (self.description_messages,
- 300, 120,
- DESCRIPTION_DELAY, "%s")
- welcome_label.connect ("button-press-event",
- descriptions_label.on_button_press)
- box = gtk.EventBox ()
- alignment = gtk.Alignment (0.5, 0.5)
- alignment.set_padding (10, 10, 0, 0)
- alignment.add (descriptions_label)
- box.connect ("button-press-event", descriptions_label.on_button_press)
- box.add (alignment)
- main_box.pack_start (box)
-
- by_label = WindowedLabel (True)
- by_label.set_markup ("<big><b>%s</b></big>" % _("Brought to you by:"))
- main_box.pack_start (by_label)
-
- alignment = gtk.Alignment (0.5, 0.5)
- '''Realize the header right now to get everything (especially the
- contributors list) correctly positionned and sized.'''
- self.header.realize ()
- width = self.header.width
- label = HorzAnimatedLabel (self.contributors, width,
- 30, CONTRIBUTOR_DELAY, "<b>%s</b>")
- by_label.connect ("button-press-event", label.on_button_press)
- label.show_all ()
- alignment.add (label)
-
- main_box.pack_start (alignment)
-
- '''System info labels'''
- def make_info_label (info):
- if not info[1]:
- return False
- label = gtk.Label ()
- infos_dict = {"name": info[0], "value": info[1]}
- # Translators: %(name)s and %(value)s should not be translated:
- # it's a way to identify a string, so just handle them like %s
- label.set_markup (_("<b>%(name)s:</b> %(value)s") % infos_dict)
- label.set_selectable (True)
- label.connect ("focus-out-event",
- lambda l, e: l.select_region (-1, -1))
- alignment = gtk.Alignment (0, 0.5)
- alignment.set_padding (0, 4, 8, 0)
- alignment.add (label)
- return alignment
-
- info_labels = map (make_info_label, self.system_infos)
- info_labels = filter (lambda l: l <> False, info_labels)
- map (lambda l: main_box.pack_start (l, False, False), info_labels)
-
- main_box.show_all ()
-
- def get_system_infos (self):
- '''Fetch various system infos'''
- file = locate_file ("gnome-version.xml")
- if not file:
- print '''Warning: "gnome-version.xml" file not found.'''
- return []
-
- f = open (file, "r")
- try:
- document = xml.dom.minidom.parse (f)
- finally:
- f.close ()
-
- if document.firstChild.nodeName != "gnome-version":
- print '''Warning: corrupted "gnome-version.xml".'''
- return []
-
- infos = {
- "platform" : "",
- "minor" : "",
- "micro" : "",
- "distributor" : "",
- "date" : ""
- }
-
- for node in document.firstChild.childNodes:
- if node.nodeName in infos:
- infos[node.nodeName] = node.firstChild.nodeValue
- elif node.nodeName == "description":
- self.load_description_messages (node)
-
- '''Format version'''
- if not len (infos["platform"]):
- version = ""
- elif not len (infos["minor"]):
- version = infos["platform"]
- elif not len (infos["micro"]):
- version = "%s.%s" % (infos["platform"], infos["minor"])
- else:
- version = "%s.%s.%s" % (infos["platform"], infos["minor"],
- infos["micro"])
-
- date = cleanup_date (infos["date"])
-
- retval = []
- if version:
- retval.append((_("Version"), version))
- if infos["distributor"]:
- retval.append((_("Distributor"), infos["distributor"]))
- if date:
- retval.append((_("Build Date"), date))
-
- return retval
-
- def load_description_messages (self, node):
- '''Find the best translation of each description message'''
- languages = get_language_names () + [""]
- def desc_filter_func (node):
- '''Helper filter function for XML descriptions'''
- return node.nodeName == "p" \
- and node.getAttribute ("xml:lang") in languages
- def desc_sort_func (a, b):
- '''Helper sorting function to sort translated messages'''
- return cmp (languages.index (a), languages.index (b))
- descs = filter (desc_filter_func, node.childNodes)
- raw_descs = filter (lambda n: n.getAttribute ("xml:lang") == "", descs)
- i = -1
- translations = []
- for desc in raw_descs:
- new_i = descs.index (desc)
- if i != - 1:
- # Append the previous description
- translations.append (descs[i:new_i])
- i = new_i
- # Open ended to retrieve all translation for last description
- translations.append (descs[i:])
- messages = GettableList ()
- for block in translations:
- sorted_descs = sorted (block, cmp = desc_sort_func,
- key = lambda n: n.getAttribute ("xml:lang"))
- best = sorted_descs[0].firstChild.nodeValue
- messages.append (best)
- self.description_messages = messages
-
- def response_callback (self, widget, response):
- '''Handle dialog response when Close button is triggered'''
- if response == gtk.RESPONSE_CLOSE:
- gtk.main_quit ()
-
- if __name__ == "__main__":
- parser = OptionParser (
- option_list = [
- make_option ("--gnome-version",
- action="store_true",
- dest="gnome_version",
- help=_("Display information on this GNOME version")),
- # FIXME: remove this when we can get all the default
- # options
- make_option ("--version",
- action="store_true",
- dest="version",
- help=VERSION),
- ])
- #FIXME: http://bugzilla.gnome.org/show_bug.cgi?id=496278
- parser.parse_args (sys.argv)
- if parser.values.gnome_version:
- about = GnomeAbout (ui = False)
- about.gnome_version ()
- elif parser.values.version:
- print "GNOME gnome-about %s" % (VERSION)
- else:
- about = GnomeAbout ()
- about.show_all ()
- try:
- gtk.main ()
- except KeyboardInterrupt:
- pass
-